package com.zhj.agent.interceptor.doubbo;

import com.zhj.agent.common.ConstName;
import com.zhj.agent.core.RuntimeContextManager;
import com.zhj.agent.support.InstanceMethodsAroundInterceptor;

import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcContext;

import java.lang.reflect.Method;

/**
 * dubbo拦截类
 */
public class DubboInterceptor implements InstanceMethodsAroundInterceptor {

    /**
     * Before method
     *
     * @param objInst        obj inst
     * @param method         method
     * @param allArguments   all arguments
     * @param argumentsTypes arguments types
     * @throws Throwable throwable
     * @since y.y.y
     */
    @Override
    public void beforeMethod(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes) throws Throwable {
        Invoker invoker = (Invoker) allArguments[0];
        Invocation invocation = (Invocation) allArguments[1];
        RpcContext rpcContext = RpcContext.getContext();
        boolean isConsumer = rpcContext.isConsumerSide();
        URL requestURL = invoker.getUrl();
        if (isConsumer) {
            //添加一次当前服务的名称
            RuntimeContextManager.add(RuntimeContextManager.CarrierItem.item(System.getProperty(ConstName.CONFIG_APP_NAME),
                                                                             System.getProperty(ConstName.CONFIG_APP_NAME)));
            //判断如果是消费者，将数据写入请求头
            String traceStr = RuntimeContextManager.getTraceStr();
            rpcContext.getAttachments().put(ConstName.HEAD_KEY, traceStr);
        } else {
            //如果是提供者
            String traceStr = rpcContext.getAttachments().get(ConstName.HEAD_KEY);
            RuntimeContextManager.addTraceForStr(traceStr);
        }
    }

    /**
     * After method
     *
     * @param objInst        obj inst
     * @param method         method
     * @param allArguments   all arguments
     * @param argumentsTypes arguments types
     * @param result         result
     * @return the object
     * @throws Throwable throwable
     * @since y.y.y
     */
    @Override
    public Object afterMethod(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object result) throws Throwable {
        return null;
    }

    /**
     * Handle method exception
     *
     * @param objInst        obj inst
     * @param method         method
     * @param allArguments   all arguments
     * @param argumentsTypes arguments types
     * @param t              t
     * @since y.y.y
     */
    @Override
    public void handleMethodException(Object objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {

    }

    /**
     * Generate operation name
     *
     * @param requestURL request url
     * @param invocation invocation
     * @return the string
     * @since y.y.y
     */
    private String generateOperationName(URL requestURL, Invocation invocation) {
        StringBuilder operationName = new StringBuilder();
        String groupStr = requestURL.getParameter(Constants.GROUP_KEY);
        groupStr = StringUtils.isEmpty(groupStr) ? "" : groupStr + "/";
        operationName.append(groupStr);
        operationName.append(requestURL.getPath());
        operationName.append("." + invocation.getMethodName() + "(");
        for (Class<?> classes : invocation.getParameterTypes()) {
            operationName.append(classes.getSimpleName() + ",");
        }
        if (invocation.getParameterTypes().length > 0) {
            operationName.delete(operationName.length() - 1, operationName.length());
        }
        operationName.append(")");
        return operationName.toString();
    }

    /**
     * Generate request url
     *
     * @param url        url
     * @param invocation invocation
     * @return the string
     * @since y.y.y
     */
    private String generateRequestURL(URL url, Invocation invocation) {
        StringBuilder requestURL = new StringBuilder();
        requestURL.append(url.getProtocol() + "://");
        requestURL.append(url.getHost());
        requestURL.append(":" + url.getPort() + "/");
        requestURL.append(generateOperationName(url, invocation));
        return requestURL.toString();
    }
}
